home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / basic / databas.com / DATABASE.BAS < prev    next >
Encoding:
BASIC Source File  |  1989-05-21  |  28.7 KB  |  1,017 lines

  1.  
  2. ' DATABASE.BAS
  3. ' Program to test database programming routines - Jose Garcia 04/1989
  4. ' <ESC> almost anywhere in the program returns you to the main menu
  5. ' See DATABASE.DOC for more documentation other than the comments
  6. ' ****
  7. ' NOTE use QB /AH to start QuickBASIC as memory arrays are put into the
  8. ' **** far heap as dynamic arrays outside the 64K BASIC limits
  9.  
  10. CONST false% = 0, true% = NOT false%
  11.  
  12. DECLARE FUNCTION finddeleted% (getnum%)
  13. DECLARE FUNCTION nospace$ (searchstring$)
  14. DECLARE FUNCTION search% (whatText$)
  15.  
  16. DECLARE SUB border ()
  17. DECLARE SUB box (tr%, br%, lc%, rc%, bars%)
  18. DECLARE SUB center (whichline, text$)
  19. DECLARE SUB dataentry ()
  20. DECLARE SUB db.index ()
  21. DECLARE SUB form ()
  22. DECLARE SUB delete ()
  23. DECLARE SUB displaydata ()
  24. DECLARE SUB edit ()
  25. DECLARE SUB endit ()
  26. DECLARE SUB getname ()
  27. DECLARE SUB txt.edit (temps$(), fieldrow%, fieldcol%, flag%)
  28. DECLARE SUB highlight ()
  29. DECLARE SUB menucall ()
  30. DECLARE SUB lowlight ()
  31. DECLARE SUB menu (fgd, bkgd, brdr)
  32. DECLARE SUB message ()
  33. DECLARE SUB openfile ()
  34. DECLARE SUB openindex ()
  35. DECLARE SUB pause ()
  36. DECLARE SUB reindex (target$)
  37. DECLARE SUB sort ()
  38. DECLARE SUB switch (number%)
  39. DECLARE SUB updatendx ()
  40. DECLARE SUB yesorno ()
  41.  
  42. TYPE address                'initializes data file fields
  43.   first AS STRING * 10
  44.   last AS STRING * 15
  45.   address1 AS STRING * 25
  46.   address2 AS STRING * 30
  47.   recnum AS INTEGER
  48. END TYPE
  49.  
  50. TYPE indexrecord            ' initializes index file fields
  51.   firstlast AS STRING * 30
  52.   recordnumber AS INTEGER
  53.   pointer AS INTEGER
  54. END TYPE
  55.  
  56. OPTION BASE 1
  57. COMMON SHARED index() AS indexrecord, temp()  AS indexrecord
  58. '$DYNAMIC  
  59. DIM SHARED names AS address, temps$(4), choice%, m$(5), np%, yn$
  60. DIM SHARED numberofrecords%, recordnumber%, counter%, found.deleted%
  61. DIM SHARED temprow%, comma$, target$, nf%, nb%, rf%, rb%
  62. comma$ = CHR$(44) + CHR$(32)
  63. ON ERROR GOTO errorhandler
  64. openfile
  65.  
  66. top:
  67. menucall
  68.  
  69. errorhandler:
  70. SELECT CASE ERR
  71.   CASE 53
  72.     db.index
  73.     RESUME top
  74.   CASE ELSE
  75.     COLOR 20, 7
  76.     BEEP
  77.     LOCATE 25, 1
  78.     PRINT "ERROR NUMBER -> "; ERR; " has occured.  Check program code";
  79.     pause
  80.     RESUME top
  81. END SELECT
  82. RESUME top
  83.  
  84. REM $STATIC
  85. SUB border
  86. LOCATE 25, 1
  87. COLOR 15, 4
  88. PRINT SPACE$(80);
  89. LOCATE 1, 1
  90. PRINT SPACE$(160);
  91.  
  92. END SUB
  93.  
  94. SUB box (tr%, br%, lc%, rc%, bars%)
  95.  
  96. ' This is a generic routine that can be used to draw a box anywhere.
  97. ' tr% is the top row. lc% is the beginning left column.
  98. ' br% is the bottom row. rc% is the ending right column.
  99. ' The paramater bars%, set to 2, prints horizontal bars
  100. ' three rows down from the top of the box and two rows up from the bottom.
  101. ' If bars% is set to 1, the routine will print an outlined box.
  102. ' If bars% is set to 0, just a plain box is printed (be sure to change color
  103. ' before the call to box so that the box will be visible).
  104.  
  105.   middle% = rc% - lc%
  106.   lines% = tr%
  107.   boxside$ = CHR$(186)
  108.   boxtop% = (rc% - lc%) - 1
  109.   boxtop$ = CHR$(201) + STRING$(boxtop%, 205) + CHR$(187)
  110.   boxbottom$ = CHR$(200) + STRING$(boxtop%, 205) + CHR$(188)
  111.   midbox$ = CHR$(204) + STRING$(boxtop%, 205) + CHR$(185)
  112.   FOR boxsize% = tr% TO br%
  113.     LOCATE lines%, lc%, 0
  114.     PRINT SPACE$(middle%);
  115.     lines% = lines% + 1
  116.   NEXT
  117.   IF bars% = 0 THEN EXIT SUB
  118.   IF bars% = 1 THEN
  119.     LOCATE tr%, lc%
  120.     PRINT boxtop$;
  121.     FOR outline% = tr% + 1 TO br% - 1
  122.       LOCATE outline%, lc%
  123.       PRINT boxside$;
  124.       LOCATE outline%, rc%
  125.       PRINT boxside$;
  126.     NEXT outline%
  127.     LOCATE br%, lc%
  128.     PRINT boxbottom$;
  129.   END IF
  130.   IF bars% = 2 THEN   'Prints optional top and bottom bars in box
  131.      LOCATE tr% + 3, lc%: PRINT midbox$;
  132.      LOCATE br% - 2, lc%: PRINT midbox$;
  133.   END IF
  134.  
  135. END SUB
  136.  
  137. SUB center (whichline, text$)
  138.  'This is a simple routine that centers a string of text text$
  139.  'on line number WHICHLINE.
  140.  
  141.   text% = LEN(text$)
  142.   text% = INT((80 - text%) / 2)
  143.   LOCATE whichline, text%
  144.   PRINT text$;
  145.  
  146. END SUB
  147.  
  148. SUB dataentry
  149. ' The TYPE variables are assigned to the array temps$(),
  150. ' txt.edit is called for data entry, then the temporary array variables
  151. ' are switched back to their TYPE'd names.  The search% function sees if
  152. ' the record is already in the index memory array.  The finddeleted% function
  153. ' checks for a deleted record and dataentry will put the new record into the
  154. ' database record file with the deleted record's record number, thus deleting
  155. ' the deleted record physically from the file.  See comments in DELETE sub.
  156. COLOR 15, 1
  157. CLS
  158. message
  159. COLOR 0, 3
  160. form
  161. temps$(1) = names.first
  162. temps$(2) = names.last
  163. temps$(3) = names.address1
  164. temps$(4) = names.address2
  165. recordnumber% = numberofrecords% + 1
  166. txt.edit temps$(), 9, 35, 1
  167. switch recordnumber%
  168.  
  169. correct:
  170. COLOR 14, 0
  171. LOCATE 17, 32
  172. PRINT "Correct ? Y/N ";
  173. ans$ = INPUT$(1)
  174. IF UCASE$(ans$) = "Y" THEN
  175.   last$ = names.last
  176.   first$ = names.first
  177.   target$ = nospace$(last$) + comma$ + nospace$(first$)
  178.   IF numberofrecords% <> 0 THEN
  179.     inFile% = search%(target$)
  180.   END IF
  181.  
  182.   IF inFile% THEN
  183.     LOCATE 20, 25
  184.     PRINT "That name is already on file";
  185.     BEEP
  186.    pause
  187.     GOTO enddataentry
  188.   END IF
  189.   found.deleted% = 0
  190.   found.deleted% = finddeleted%(getnum%)
  191.  
  192.   IF found.deleted% THEN
  193.       numberofrecords% = numberofrecords% - 1
  194.     recordnumber% = found.deleted%
  195.   END IF
  196.  
  197.   PUT #1, recordnumber%, names
  198.   numberofrecords% = numberofrecords% + 1
  199.   reindex (target$)
  200.  
  201. ELSEIF UCASE$(ans$) = "N" THEN
  202.   LOCATE 17, 30
  203.   COLOR 1, 1
  204.   PRINT SPACE$(15)
  205.   COLOR 0, 3
  206.   txt.edit temps$(), 9, 35, 0
  207.   LOCATE , , 1, 7
  208.   GOTO correct
  209. ELSE
  210.   GOTO correct
  211. END IF
  212. enddataentry:
  213.  
  214. END SUB
  215.  
  216. SUB db.index
  217.     ' This routine can also be used in programs to reindex an .ndx file
  218.     ' (created with the routines in this program) that has become corrupt.
  219.     ' The database record file has to be intact.
  220.     CLOSE
  221.     CLS
  222.     COLOR 15, 0
  223.     center 12, "INDEXING DATABASE FILE"
  224.     OPEN "names.dat" FOR RANDOM AS #1 LEN = (LEN(names))
  225.     numberofrecords% = LOF(1) \ LEN(names)
  226.     FOR i% = 1 TO numberofrecords%
  227.       GET #1, i%, names
  228.       index(i%).firstlast = UCASE$(LTRIM$(RTRIM$(names.last))) + comma$ + UCASE$(LTRIM$(RTRIM$(names.first)))
  229.       index(i%).recordnumber = names.recnum
  230.     NEXT i%
  231.     sort
  232.     updatendx
  233.     CLOSE #1
  234.     openfile
  235. END SUB
  236.  
  237. SUB delete
  238. ' This routine does not physically delete records from the database record
  239. ' file.  Instead, it enters an ASCII 20 character for the first character
  240. ' of the indexed record and the database record.  These records are overlooked
  241. ' by any routines that display records or indexes, thus "deleting" them from
  242. ' the files.  They are physically removed when new records are added because
  243. ' they are overwritten by the new records.
  244. COLOR 15, 1
  245. CLS
  246. border
  247. center 25, "Delete this record ?  Y/N "
  248. COLOR 0, 3
  249. form
  250. LOCATE 9, 35
  251. PRINT names.first
  252. LOCATE 10, 35
  253. PRINT names.last
  254. LOCATE 11, 35
  255. PRINT names.address1
  256. LOCATE 12, 35
  257. PRINT names.address2;
  258. LOCATE 25, 60
  259. yesorno
  260. IF yn$ = "N" THEN
  261.   EXIT SUB
  262. ELSEIF yn$ = "Y" THEN
  263.   names.last = CHR$(20) + MID$(names.last, 2)
  264.   PUT #1, temp(counter%).recordnumber, names
  265.   counter% = temp(counter%).pointer
  266.   index(counter%).firstlast = CHR$(20) + RTRIM$(LTRIM$(MID$(names.last, 2))) + comma$ + RTRIM$(LTRIM$(MID$(names.first, 1)))
  267.   index(counter%).recordnumber = names.recnum
  268.   sort
  269. END IF
  270. END SUB
  271.  
  272. SUB displaydata
  273. LOCATE 25, 1
  274. COLOR 15, 4
  275. PRINT SPACE$(80);
  276. COLOR 15, 1
  277. CLS
  278. COLOR 0, 3
  279. form
  280. LOCATE 9, 35
  281. PRINT names.first
  282. LOCATE 10, 35
  283. PRINT names.last
  284. LOCATE 11, 35
  285. PRINT names.address1
  286. LOCATE 12, 35
  287. PRINT names.address2
  288. pause
  289.  
  290. END SUB
  291.  
  292. SUB edit STATIC
  293. ' This routine calls txt.edit with the flag% value of 0 which means that the
  294. ' txt.edit sub starts in the overwrite mode as opposed to the insert mode
  295. ' that it starts in when flag% is set to 1 in the dataentry sub.
  296. ' I have not devised a method of checking for double entry if the first and
  297. ' last name are already on file because if data other than the names were
  298. ' changed here, the first and last names are already on file in the original
  299. ' copy of the record and the double entry check used in the DATAENTRY routine
  300. ' would not work here because it would always find a double entry.
  301. ' This is a good place for improvement.
  302. COLOR 15, 1
  303. CLS
  304. message
  305. COLOR 0, 3
  306. form
  307. LOCATE 9, 35
  308. PRINT names.first
  309. LOCATE 10, 35
  310. PRINT names.last
  311. LOCATE 11, 35
  312. PRINT names.address1
  313. LOCATE 12, 35
  314. PRINT names.address2;
  315.  
  316. temps$(1) = names.first
  317. temps$(2) = names.last
  318. temps$(3) = names.address1
  319. temps$(4) = names.address2
  320.  
  321. txt.edit temps$(), 9, 35, 0
  322. switch names.recnum%
  323.  
  324. counter% = temp(counter%).pointer
  325. index(counter%).firstlast = UCASE$(LTRIM$(RTRIM$(names.last))) + comma$ + UCASE$(LTRIM$(RTRIM$(names.first)))
  326. PUT #1, names.recnum, names
  327. sort
  328. LOCATE , , 1, 7
  329. END SUB
  330.  
  331. SUB endit
  332.  
  333. updatendx
  334. CLOSE
  335. COLOR 0, 7
  336. CLS
  337. COLOR 11, 0
  338. LOCATE 10, 27
  339. PRINT "■ MicroComputer Services ■"
  340. END
  341.  
  342. END
  343.  
  344. END SUB
  345.  
  346. FUNCTION finddeleted% (getnum%)
  347. ' This routine searches the index array for records starting with ASCII 20
  348. ' and returns a non-zero (true) value to the dataentry sub.  This indicates
  349. ' to dataentry that there is a deleted record and it's record number is
  350. ' finddeleted% and to store the new record in the record number finddeleted%
  351. getnum% = 0
  352. del$ = CHR$(20)
  353. FOR x% = 1 TO numberofrecords%
  354.   indexname$ = index(x%).firstlast
  355.   IF del$ = LEFT$(indexname$, 1) THEN
  356.     getnum% = index(x%).recordnumber
  357.     index(x%).firstlast = target$
  358.     EXIT FOR
  359.   END IF
  360. NEXT x%
  361.  
  362. finddeleted% = getnum%
  363.  
  364.  
  365. END FUNCTION
  366.  
  367. SUB form
  368. 'Prints a rectangle with name and address blank form.
  369. box 8, 13, 15, 65, 1
  370. FOR x% = 9 TO 12
  371.   LOCATE x%, 16
  372.   PRINT SPACE$(49);
  373. NEXT x%
  374. COLOR 0, 3
  375. LOCATE 9, 16
  376. PRINT "First Name:"
  377. LOCATE 10, 16
  378. PRINT "Last Name: "
  379. LOCATE 11, 16
  380. PRINT "Address: "
  381. LOCATE 12, 16
  382. PRINT "City, State  ZIP"
  383.  
  384.  
  385. END SUB
  386.  
  387. SUB getname STATIC
  388. 'This routine puts 15 sorted names from the index into a box and allows the
  389. 'user to choose one with a bounce-bar.  User can see the next page or the
  390. 'previous page by using <N> and <P>,  correct name is highlighted and
  391. '<RETURN> is pressed for record selection
  392.  
  393. nf% = 15: nb% = 1: rf% = 1: rb% = 7   'color assignment normal and reversed
  394.  
  395.  
  396. top2: '*****************
  397. COLOR , 0: CLS
  398. border
  399. LOCATE 25, 30
  400. PRINT "Last Name: ";
  401. LINE INPUT ; lastname$
  402. COLOR 15, 4
  403. LOCATE 25, 27
  404. PRINT "<"; CHR$(24); "> or <"; CHR$(25); "> KEYS + ENTER";
  405. COLOR nf%, nb%
  406. box 5, 19, 25, 55, 0
  407. counter% = 1
  408. leng% = LEN(lastname$)
  409. start% = 1
  410. '***********************
  411. searchstart:
  412. row% = 5
  413. second.counter% = 0
  414. REDIM temp(1 TO UBOUND(index, 1)) AS indexrecord
  415. '***********************
  416. xstart:
  417. FOR array% = start% TO UBOUND(index, 1)
  418.     second.counter% = second.counter% + 1
  419.     IF UCASE$(MID$(index(array%).firstlast, 1, leng%)) = UCASE$(lastname$) THEN
  420.       IF UCASE$(LEFT$(index(array%).firstlast, 1)) = CHR$(20) THEN GOTO again
  421.       LOCATE row%, 25
  422.       PRINT index(array%).firstlast
  423.       temp(counter%).firstlast = index(array%).firstlast
  424.       temp(counter%).recordnumber = index(array%).recordnumber
  425.       temp(counter%).pointer = array%
  426.       row% = row% + 1
  427.       counter% = counter% + 1
  428.     IF counter% > 15 THEN
  429. choice:         '***********
  430.         LOCATE 25, 1
  431.         COLOR 15, 4
  432.         PRINT SPACE$(80);
  433.         LOCATE 25, 5
  434.         PRINT " <N>ext screen, <P>revious screen, <"; CHR$(24); "> , <"; CHR$(25); ">  <ESC> to end";
  435.         GOTO bouncebar
  436.     END IF
  437.   END IF
  438. again:
  439. NEXT array%
  440. IF counter% = 0 THEN
  441.   COLOR 4, 7
  442.   LOCATE , , 0
  443.   center 25, "That name is not on file...  Please choose again"
  444.   pause
  445.   GOTO top2
  446. END IF
  447.  
  448. bouncebar:   '************************** prints the highlighted bounce bar
  449. temprow% = 5
  450. endcounter% = counter% - 1
  451. counter% = 1
  452. IF temp(counter%).recordnumber = 0 THEN
  453.   COLOR 15, 4
  454.   center 25, "That name is not on file...   Please choose again"
  455.   COLOR nf%, nb%
  456.   pause
  457.   GOTO top2
  458. END IF
  459.  
  460. highlight
  461. PRINT temp(counter%).firstlast;
  462.  
  463. DO
  464.   DO
  465.     keystroke$ = INKEY$
  466.   LOOP WHILE keystroke$ = ""
  467.  
  468.   tempkey% = ASC(RIGHT$(keystroke$, 1))
  469.   tempscankey% = ASC(LEFT$(keystroke$, 1))
  470.  
  471.   IF tempscankey% = 0 THEN
  472.     SELECT CASE tempkey%
  473.       CASE 72                           ' <UP>
  474.         IF temprow% = 5 THEN
  475.           lowlight
  476.           PRINT temp(counter%).firstlast;
  477.           temprow% = row% - 1
  478.           counter% = endcounter%
  479.           highlight
  480.           PRINT temp(counter%).firstlast;
  481.           
  482.         ELSE
  483.           temprow% = CSRLIN
  484.           lowlight
  485.           PRINT temp(counter%).firstlast;
  486.           counter% = counter% - 1
  487.           temprow% = temprow% - 1
  488.           highlight
  489.           PRINT temp(counter%).firstlast;
  490.           
  491.         END IF
  492.       CASE 80      ' down
  493.        temprow% = CSRLIN
  494.         
  495.         IF temprow% = row% - 1 THEN
  496.           counter% = 1
  497.           temprow% = row% - 1
  498.           lowlight
  499.           PRINT temp(endcounter%).firstlast;
  500.           temprow% = 5
  501.           counter% = 1
  502.           highlight
  503.           PRINT temp(counter%).firstlast;
  504.          
  505.         ELSE
  506.           lowlight
  507.           PRINT temp(counter%).firstlast;
  508.           counter% = counter% + 1
  509.           temprow% = temprow% + 1
  510.           highlight
  511.           PRINT temp(counter%).firstlast;
  512.       END IF
  513.     END SELECT
  514.   ELSEIF tempscankey% <> 0 THEN
  515.     SELECT CASE UCASE$(keystroke$)
  516.       CASE IS = CHR$(27)
  517.         VIEW PRINT
  518.         menucall
  519.       CASE IS = "P"
  520.         COLOR nf%, nb%
  521.         box 5, 19, 25, 55, 0
  522.         counter% = 1
  523.         row% = 5
  524.         start% = start% - 15
  525.         second.counter% = 0
  526.         IF start% < 1 THEN
  527.           COLOR 15, 4
  528.           LOCATE 25, 1
  529.           PRINT SPACE$(80);
  530.           center 25, "Beginning of file"
  531.           BEEP
  532.           pause
  533.           start% = 1
  534.           COLOR nf%, nb%
  535.         END IF
  536.         GOTO xstart
  537.       CASE IS = "N"
  538.         IF array% > UBOUND(index, 1) THEN
  539.           COLOR 15, 4
  540.           LOCATE 25, 1
  541.           PRINT SPACE$(80);
  542.           center 25, "End of database file"
  543.           BEEP
  544.           pause
  545.           LOCATE 25, 1
  546.           PRINT SPACE$(80);
  547.           LOCATE 25, 5
  548.           PRINT " <N>ext screen, <P>revious screen, <"; CHR$(24); "> , <"; CHR$(25); ">  <ESC> to end";
  549.           GOTO bouncebar
  550.         END IF
  551.        
  552.         COLOR nf%, nb%
  553.         box 5, 19, 25, 55, 0
  554.         start% = second.counter% + 1
  555.         second.counter% = 0
  556.         counter% = 1
  557.         row% = 5
  558.         GOTO xstart
  559.     END SELECT
  560.   END IF
  561. LOOP UNTIL keystroke$ = CHR$(13)
  562. VIEW PRINT
  563. GET #1, temp(counter%).recordnumber, names
  564.  
  565. END SUB
  566.  
  567. SUB highlight
  568.   COLOR rf%, rb%
  569.   LOCATE temprow%, 25, 0
  570.   PRINT SPACE$(30);
  571.   LOCATE temprow%, 25, 0
  572. END SUB
  573.  
  574. SUB lowlight
  575.   COLOR nf%, nb%
  576.   LOCATE temprow%, 25, 0
  577.   PRINT SPACE$(30);
  578.   LOCATE temprow%, 25, 0
  579. END SUB
  580.  
  581. SUB menu (fgd, bkgd, brdr)
  582.  
  583. 'This is the famous bar-menu routine by Frank R. Neal of Columbus, Ohio.
  584. 'For specifics on it see one of the many QB menu programs on Compuserve, they
  585. 'all use a form of this routine.
  586. 'It returns the user's choice in the variable CHOICE%. Note that
  587. 'CHOICE% must be DIM'ed as a SHARED variable at the beginning of the program
  588. 'as does NP%.  NP% equals the number of menu choices available.
  589. 'The FBD, BKGD and BRDR parameters are the foreground, background and
  590. 'border colors of the menu printing. Note that BRDR = border color and is not
  591. 'supported by EGA VGA and MCGA adapters. see QuickBASIC version 4.0 BASIC
  592. 'Language Reference manual page 110.
  593.  
  594.  
  595. COLOR fgd, bkgd, brdr
  596. step1:
  597.       row = 8: col = 20: '     SET ROW AND COLUMN FOR MENU
  598.       C1F = fgd: C1B = bkgd'   SET COLOR CODES
  599.       C2F = bkgd: C2B = fgd: '       SET BAR COLOR TO COLOR 0,2
  600. step2:
  601.  
  602. GOSUB step3
  603.       CLS
  604.       GOTO menu.end
  605.       GOTO step1
  606.       GOTO step2
  607. step3:
  608.       COLOR C1F, C1B
  609.       FOR J = 1 TO 16: x$ = INKEY$:  NEXT: choice% = 1
  610.       LS = 2: FOR J = 1 TO np%: IF LEN(m$(J)) > LS THEN LS = LEN(m$(J))
  611.       NEXT: ML$ = "##  \" + SPACE$(LS - 1) + "\": SL = col + 18 - LEN(ML$) / 2
  612.       FOR K = 1 TO np%: LOCATE row + 2 + K, SL: PRINT USING ML$; K; m$(K): NEXT
  613. step4:
  614.   LOCATE row + 2 + choice%, SL: COLOR C2F, C2B: PRINT USING ML$; choice%; m$(choice%): COLOR C1F, C1B: TD = choice%
  615. step5:
  616.       x$ = INKEY$: IF LEN(x$) THEN KP = ASC(RIGHT$(x$, 1)) ELSE GOTO step5
  617.       IF KP = 72 THEN choice% = choice% - 1: IF choice% < 1 THEN choice% = np%
  618.       IF KP = 80 THEN choice% = choice% + 1: IF choice% > np% THEN choice% = 1
  619.       IF x$ >= "1" AND x$ <= "9" THEN IF VAL(x$) >= 1 AND VAL(x$) <= np% THEN choice% = VAL(x$): RETURN
  620.       IF KP = 13 THEN RETURN
  621.       IF KP <> 72 AND KP <> 80 THEN KP = KP - 48: IF KP < 1 OR KP > np% THEN PRINT CHR$(7): GOTO step5 ELSE choice% = KP
  622.       IF choice% = TD THEN GOTO step5 ELSE LOCATE row + 2 + TD, SL: PRINT USING ML$; TD; m$(TD): GOTO step4
  623. menu.end:
  624.  
  625.  
  626.  
  627. END SUB
  628.  
  629. SUB menucall STATIC
  630. 'This starts the Main Menu of the program
  631.  
  632. thetop:
  633.  
  634. np% = 5
  635. m$(1) = "Add a record"
  636. m$(2) = "Find a record"
  637. m$(3) = "Edit a record"
  638. m$(4) = "Delete a record"
  639. m$(5) = "Quit"
  640.  
  641. COLOR 14, 1
  642. CLS
  643. box 9, 17, 24, 51, 1
  644. COLOR 15, 4
  645. LOCATE 2, 25
  646. PRINT "ADDRESS DATABASE MAIN MENU"
  647. LOCATE 25, 1
  648. COLOR 15, 4
  649. FOR x% = 1 TO 80
  650.   PRINT CHR$(32);
  651. NEXT x%
  652. LOCATE 25, 27
  653. PRINT "<"; CHR$(24); "> or <"; CHR$(25); "> KEYS + ENTER";
  654. COLOR 14, 1
  655. LOCATE , , 0
  656. menu 7, 1, 1
  657. LOCATE , , 1
  658. ON choice% GOTO datain, find, edit, delete, enditall
  659.  
  660. datain:
  661. dataentry
  662. GOTO thetop
  663.  
  664. find:
  665. getname
  666. displaydata
  667. GOTO thetop
  668.  
  669. edit:
  670. getname
  671. edit
  672. GOTO thetop
  673.  
  674. delete:
  675. getname
  676. delete
  677. GOTO thetop
  678.  
  679. enditall:
  680. endit
  681.  
  682. END SUB
  683.  
  684. SUB message
  685. COLOR 15, 4
  686. LOCATE 25, 1
  687. PRINT "CURSOR KEYS:<"; CHR$(24); "> <"; CHR$(25); "> <"; CHR$(27); "> <"; CHR$(26); "> <INS> <DEL> <HOME> <END> <PGUP> <PGDN> ;<ESC> aborts";
  688. END SUB
  689.  
  690. FUNCTION nospace$ (searchstring$)
  691. ' This function removes any spaces from the index arrays.
  692.  
  693. searchstring$ = UCASE$(LTRIM$(RTRIM$(searchstring$)))
  694. tempstring$ = ""
  695. FOR i% = 1 TO LEN(searchstring$)
  696.   oneChar$ = MID$(searchstring$, i%, 1)
  697.   IF oneChar$ <> CHR$(32) THEN
  698.     tempstring$ = tempstring$ + oneChar$
  699.   END IF
  700. NEXT i%
  701. nospace$ = tempstring$
  702. END FUNCTION
  703.  
  704. SUB openfile
  705.  
  706. OPEN "names.dat" FOR RANDOM AS #1 LEN = (LEN(names))
  707. numberofrecords% = LOF(1) \ LEN(names)
  708. recordnumber% = numberofrecords%
  709. IF recordnumber% > 0 THEN openindex
  710.  
  711. END SUB
  712.  
  713. SUB openindex
  714. REDIM index(numberofrecords%) AS indexrecord
  715. OPEN "names.ndx" FOR INPUT AS #2
  716. FOR indexnum% = 1 TO numberofrecords%
  717.   INPUT #2, index(indexnum%).firstlast, index(indexnum%).recordnumber
  718. NEXT indexnum%
  719. CLOSE #2
  720. END SUB
  721.  
  722. SUB pause
  723. WHILE INKEY$ = ""
  724. WEND
  725. END SUB
  726.  
  727. SUB reindex (target$) STATIC
  728. 'This routine adds the new index entry to the index array.
  729.  
  730. IF found.deleted% = false% THEN
  731.   IF numberofrecords% > 1 THEN
  732.     oldtot% = numberofrecords% - 1
  733.     REDIM tempindex(oldtot%) AS indexrecord
  734.     FOR i% = 1 TO oldtot%
  735.       tempindex(i%) = index(i%)
  736.     NEXT i%
  737.   END IF
  738.  
  739.   REDIM index(numberofrecords%)  AS indexrecord
  740.  
  741.   IF numberofrecords% > 1 THEN
  742.  
  743.     FOR i% = 1 TO oldtot%
  744.       index(i%) = tempindex(i%)
  745.     NEXT i%
  746.   END IF
  747.   index(numberofrecords%).firstlast = target$
  748.   index(numberofrecords%).recordnumber = numberofrecords%
  749. END IF
  750.  
  751.   sort
  752.  
  753. END SUB
  754.  
  755. FUNCTION search% (whatText$) STATIC
  756. ' This routine searches the index array for duplicate entries
  757. begin% = 1
  758. ending% = numberofrecords%
  759. located% = false%
  760. getnum% = 0
  761. DO WHILE begin% <= ending% AND NOT located%
  762.   middle% = (begin% + ending%) \ 2
  763.   indexname$ = RTRIM$(index(middle%).firstlast)
  764.   IF whatText$ = indexname$ THEN
  765.     located% = true%
  766.     getnum% = index(middle%).recordnumber
  767.   ELSEIF whatText$ > indexname$ THEN
  768.     begin% = middle% + 1
  769.   ELSE
  770.     ending% = middle% - 1
  771.   END IF
  772. LOOP
  773. search% = getnum%
  774.  
  775. END FUNCTION
  776.  
  777. SUB sort STATIC
  778. ' SHELL SORT routine
  779. length% = numberofrecords%
  780. jump% = 1
  781. DO WHILE jump% <= length%
  782.   jump% = jump% * 2
  783. LOOP
  784. DO WHILE jump% > 1
  785.   jump% = (jump% - 1) \ 2
  786.   DO
  787.     finished% = true%
  788.     FOR upper% = 1 TO length% - jump%
  789.       lower% = upper% + jump%
  790.       IF index(upper%).firstlast > index(lower%).firstlast THEN
  791.         SWAP index(upper%), index(lower%)
  792.         finished% = false%
  793.       END IF
  794.     NEXT upper%
  795.   LOOP UNTIL finished%
  796. LOOP
  797. END SUB
  798.  
  799. SUB switch (number%)
  800. names.first = temps$(1)
  801. names.last = temps$(2)
  802. names.address1 = temps$(3)
  803. names.address2 = temps$(4)
  804. names.recnum = number%
  805. END SUB
  806.  
  807. SUB txt.edit (temps$(), fieldrow%, fieldcol%, flag%) STATIC
  808.  
  809. 'This routine saves keyboard entry into a memory array, it allows full cursor
  810. 'control with up & down arrow, page up & down, insert, home, end, right and
  811. ' left arrows.
  812. ' flag% is either 1 or 0, cursor starts in insert mode or overwrite mode
  813. rownum% = fieldrow%                               'constant row number
  814. colnum% = fieldcol%                               'constant column number
  815. IF flag% THEN
  816.   FOR num% = 1 TO UBOUND(temps$, 1)                  ' add a space to each field
  817.     temps$(num%) = CHR$(32) + MID$(temps$(num%), 2)   ' to use later for testing
  818.   NEXT num%                                         ' if field contains data
  819. END IF
  820. num% = 1
  821. DO WHILE num% <= UBOUND(temps$, 1)
  822.   IF num% > UBOUND(temps$, 1) THEN EXIT DO
  823.     length% = LEN(temps$(num%))
  824.   IF LEFT$(temps$(num%), 1) = CHR$(32) THEN        'is the field empty, if so
  825.     temps$(num%) = SPACE$(length%)                 ' add a value to temps$(num%)
  826.   ELSE
  827.   END IF
  828.   IF flag% = 1 THEN                              'insert mode
  829.     LOCATE fieldrow%, fieldcol%, 1, 7
  830.   ELSE                                           'overstrike mode
  831.     LOCATE fieldrow%, fieldcol%, 1, 0, 7
  832.   END IF
  833.   entry$ = ""
  834.   DO UNTIL entry$ = CHR$(13) OR entry$ = CHR$(0) + CHR$(72) OR entry$ = CHR$(0) + CHR$(80) OR entry$ = CHR$(0) + CHR$(73) OR entry$ = CHR$(0) + CHR$(81)
  835.       DO
  836.         entry$ = INKEY$             'get keyboard entry
  837.       LOOP WHILE entry$ = ""
  838.       keys% = ASC(RIGHT$(entry$, 1))
  839.       scankey% = ASC(LEFT$(entry$, 1))
  840.     IF scankey% = 0 THEN
  841.       SELECT CASE keys%
  842.         CASE 72                     '<UP ARROW> key
  843.           IF fieldrow% - 1 < rownum% THEN
  844.             num% = UBOUND(temps$, 1)
  845.             fieldrow% = rownum% + (UBOUND(temps$, 1) - 1)
  846.             LOCATE fieldrow%, colnum%
  847.             EXIT DO
  848.           ELSEIF POS(0) > colnum% + LEN(temps$(num% - 1)) THEN
  849.             num% = num% - 1
  850.             fieldrow% = fieldrow% - 1
  851.             LOCATE fieldrow%, colnum% + LEN(temps$(num%)) - 1
  852.             EXIT DO
  853.           ELSE
  854.             fieldrow% = fieldrow% - 1
  855.             num% = num% - 1
  856.             LOCATE fieldrow%, POS(0)
  857.             EXIT DO
  858.           END IF
  859.         CASE 75                    '<LEFT ARROW> key
  860.           IF POS(0) = colnum% THEN
  861.             BEEP
  862.           ELSE
  863.             LOCATE fieldrow%, POS(0) - 1
  864.           END IF
  865.           temps$(num%) = temps$(num%)
  866.         CASE 77                     '<RIGHT ARROW> key
  867.           IF POS(0) = colnum% + (LEN(temps$(num%)) - 1) THEN
  868.             LOCATE fieldrow%, POS(0)
  869.             BEEP
  870.           ELSE LOCATE fieldrow%, POS(0) + 1
  871.           END IF
  872.         CASE 80                     '<DOWN ARROW> key
  873.           IF fieldrow% >= rownum% + UBOUND(temps$, 1) - 1 THEN
  874.             num% = LBOUND(temps$, 1)
  875.             fieldrow% = rownum%
  876.             LOCATE fieldrow%, colnum%
  877.             EXIT DO
  878.           ELSEIF POS(0) > colnum% + LEN(temps$(num% + 1)) THEN
  879.             num% = num% + 1
  880.             fieldrow% = fieldrow% + 1
  881.             LOCATE fieldrow%, colnum% + LEN(temps$(num%)) - 1
  882.             EXIT DO
  883.           ELSE
  884.             num% = num% + 1
  885.             fieldrow% = fieldrow% + 1
  886.             LOCATE fieldrow%, POS(0)
  887.             EXIT DO
  888.           END IF
  889.           temps$(num%) = temps$(num%) + CHR$(32)
  890.         CASE 73                     '<PG UP> key
  891.           IF num% = LBOUND(temps$, 1) THEN
  892.             LOCATE rownum%, colnum%
  893.             EXIT DO
  894.           ELSE
  895.             num% = LBOUND(temps$, 1)
  896.             fieldrow% = rownum%
  897.             LOCATE rownum%, colnum%
  898.             EXIT DO
  899.           END IF
  900.         CASE 81                      '<PG DN> key
  901.           IF num% = UBOUND(temps$, 1) THEN
  902.             LOCATE fieldrow%, colnum%
  903.             EXIT DO
  904.           ELSE
  905.             num% = UBOUND(temps$, 1)
  906.             fieldrow% = rownum% + (UBOUND(temps$, 1) - 1)
  907.             LOCATE fieldrow%, colnum%
  908.             EXIT DO
  909.           END IF
  910.         CASE 83                     ' <DEL> key
  911.           delpos% = POS(0)
  912.           L$ = LEFT$(temps$(num%), POS(0) - colnum%)
  913.           r$ = RIGHT$(temps$(num%), (length% - (POS(0) - colnum%)) - 1)
  914.           temps$(num%) = L$ + r$ + CHR$(32)
  915.           LOCATE fieldrow%, colnum%
  916.           PRINT SPACE$(length%);
  917.           LOCATE fieldrow%, colnum%
  918.           PRINT temps$(num%);
  919.           LOCATE fieldrow%, delpos%
  920.         CASE 71                     '<HOME> key - goto beginning of field
  921.           LOCATE fieldrow%, colnum%
  922.         CASE 79                     '<END> key - goto end of data
  923.           IF (INSTR((POS(0) + 1) - colnum%, temps$(num%), CHR$(32))) <> 0 THEN
  924.             advance% = POS(0) + (INSTR(temps$(num%), CHR$(32)))
  925.             LOCATE fieldrow%, advance%
  926.           IF advance% >= length% + colnum% THEN LOCATE fieldrow%, (length% + colnum%) - 1
  927.           ELSE
  928.             LOCATE fieldrow%, (length% + colnum%) - 1
  929.           END IF
  930.         CASE 82                                   '<INS> key
  931.           IF flag% = 0 THEN                       'overstrike mode on
  932.             LOCATE fieldrow%, POS(0), , 7         'turn on insert cursor
  933.             flag% = 1                             'turn on insert mode
  934.           ELSEIF flag% = 1 THEN                   'insert mode is on
  935.             LOCATE fieldrow%, POS(0), 1, 0, 7     'turn on overstrike cursor
  936.             flag% = 0                             'turn on overstrike mode
  937.           END IF
  938.       END SELECT
  939.     ELSEIF scankey% <> 0 THEN
  940.       SELECT CASE keys%
  941.         CASE 27                                     '<ESC>
  942.           menucall
  943.         CASE 13                                     '<CR>
  944.           LOCATE fieldrow%, colnum%
  945.           PRINT temps$(num%);
  946.           fieldrow% = fieldrow% + 1                 'go to next field
  947.           num% = num% + 1
  948.           LOCATE fieldrow%, colnum%
  949.           EXIT DO
  950.         CASE 8                                      '<BKSP> key
  951.           position% = POS(0)
  952.           IF position% = colnum% THEN               'at first position
  953.             LOCATE fieldrow%, POS(0)                'in field
  954.           ELSEIF position% > colnum% THEN
  955.             L$ = LEFT$(temps$(num%), position% - colnum% - 1)
  956.             r$ = RIGHT$(temps$(num%), length% - (position% - colnum%))
  957.             temps$(num%) = L$ + r$ + CHR$(32)
  958.             LOCATE fieldrow%, colnum%
  959.             PRINT SPACE$(length%)
  960.             LOCATE fieldrow%, colnum%
  961.             PRINT temps$(num%);
  962.             LOCATE fieldrow%, position% - 1
  963.           END IF
  964.         CASE ELSE                   'entry$ is alphanumeric
  965.            IF entry$ > CHR$(20) AND entry$ < CHR$(127) THEN
  966.               IF flag% = 1 AND LEN(RTRIM$(temps$(num%))) < length% THEN  'insert mode on
  967.                 insertsite% = POS(0)                          'and check for full field
  968.                 temps$(num%) = LEFT$(temps$(num%), insertsite% - colnum%) + entry$ + MID$(temps$(num%), insertsite% - colnum% + 1)
  969.                 temps$(num%) = RTRIM$(temps$(num%)) + SPACE$(length% - LEN(RTRIM$(temps$(num%))))
  970.                 LOCATE fieldrow%, colnum%
  971.                 PRINT RTRIM$(temps$(num%))
  972.                 LOCATE fieldrow%, insertsite% + 1
  973.               ELSE                  'overwrite mode on
  974.                 PRINT entry$;
  975.                 MID$(temps$(num%), (POS(0) - colnum%), 1) = entry$
  976.               END IF
  977.            END IF
  978.             IF RIGHT$(temps$(num%), 1) <> CHR$(32) AND RIGHT$(temps$(num%), 1) <> CHR$(0) OR LEN(RTRIM$(temps$(num%))) >= length% THEN
  979.               BEEP
  980.               fieldrow% = fieldrow% + 1
  981.               num% = num% + 1
  982.               LOCATE fieldrow%, colnum%, 1
  983.               EXIT DO
  984.              END IF
  985.       END SELECT
  986.     END IF
  987.   LOOP
  988. LOOP
  989.  
  990. END SUB
  991.  
  992. SUB updatendx STATIC
  993. ' This routine updates the disk index file from the index memory array.
  994. IF numberofrecords% > 0 THEN
  995.   OPEN "names.ndx" FOR OUTPUT AS #2
  996.   FOR i% = 1 TO numberofrecords%
  997.     WRITE #2, RTRIM$(index(i%).firstlast), index(i%).recordnumber
  998.   NEXT i%
  999.   CLOSE #2
  1000. END IF
  1001. END SUB
  1002.  
  1003. SUB yesorno
  1004.  
  1005. 'This simple routine gets a "Y" or "N" response to a yes/no question
  1006. 'and returns it to the caller in variable YN$. Note that YN$ must
  1007. 'be DIM'ed as a SHARED variable at the beginning of the program.
  1008.  
  1009.  
  1010. answer.please:
  1011.      yn$ = INKEY$: IF yn$ = "" THEN GOTO answer.please
  1012.      IF INSTR("YyNn", yn$) = 0 THEN GOTO answer.please
  1013.      yn$ = UCASE$(yn$)
  1014.  
  1015. END SUB
  1016.  
  1017.